(function($) {
  $.nano = function(template, data) {
    return template.replace(/\{([\w\.]*)\}/g, function(str, key) {
      var keys = key.split("."),
        v = data[keys.shift()];
      for (var i = 0, l = keys.length; i < l; i++) v = v[keys[i]];
      return (typeof v !== "undefined" && v !== null) ? v : "";
    });
  };
  $.checkType = function(self, property, type) {
    var _property = arguments[1] || '';
    var _type = arguments[2] || '';
    return self && self.hasOwnProperty(_property) && typeof (self[_property]) == _type;
  };
  var IScrollPullUpDown = function(wrapperDom, iScrollConfig, listAdapter) {
    var iScrollConfig,
      pullDownActionHandler,
      pullUpActionHandler,
      pullDownEl,
      pullDownOffset,
      pullUpEl,
      scrollStartPos;
    var headerData,
      footerData;
    var pullThreshold = 2;
    var me = this;
    var theAdapter = listAdapter;
    var listUL = $('<ul id="iscrollUL"></ul>');
    var wrapperObj = wrapperDom;

    me.notifyDatasetChage = function(forceUpdate) {
      if (forceUpdate) {
        listUL.empty();
      }
      //listview范围内可以展示的数量
      var contentNum = $(wrapperObj).height() / theAdapter.needLineHeight() + 3; //给屏幕能容下的数量一个buffer值
      //已经展示的数量
      var showCount = listUL.children().length;
      //数据源中的数据量
      var dataCount = theAdapter.needDataCount();

      if ($.isEmptyObject(pullUpEl) || dataCount < (contentNum - 3) || !theAdapter.hasMoreData()) {
        //如果没有上拉刷新||当前屏展示的数据大于数据源中的数据量，隐藏上拉刷新
        $(pullUpEl).css("display", "none");
      } else {
        $(pullUpEl).css("display", footerData["display"]);
      }
      for (var i = showCount; i < dataCount; i++) {
        listUL.append(theAdapter.configItem(i));
      }
      if (me.myScroll) {
        setTimeout(function() {
          me.myScroll.refresh();
        }, 10);
      }
    };

    function createStatus(statusName) {
      return {
        status: statusName,
        scroller: me.myScroll,
      };
    }

    function showPullDownElNow(className) {
      // Shows pullDownEl with a given className
      $(pullDownEl).css('transitionDuration', '').css('margin-top', '').removeClass('LSflip LSLoading LSinit').addClass(className).trigger('statuschanged', createStatus(className));
    }

    var hidePullDownEl = function(time, refresh) {
      // Hides pullDownEl
      $(pullDownEl).css('transitionDuration', time > 0 ? time + 'ms' : '').css('margin-top', -pullDownOffset + 'px').removeClass('LSflip LSLoading').addClass('LSinit').trigger('statuschanged', createStatus('LSinit'));
      // If refresh==true, refresh again after time+10 ms to update iScroll's "scroller.offsetHeight" after the pull-down-bar is really hidden...
      // Don't refresh when the user is still dragging, as this will cause the content to jump (i.e. don't refresh while dragging)
      if (refresh) {
        setTimeout(function() {
          me.myScroll.refresh();
        }, time + 10);
      }
    }

    function init() {
      $(wrapperObj).append($('<div class="iScroller"></div>').append(listUL));
      var scrollerObj = $(wrapperObj).children()[0];

      if (theAdapter.needPullDownHeader) {
        var tempHeader = theAdapter.needPullDownHeader();
        if (tempHeader) {
          headerData = tempHeader;
          pullDownEl = headerData.dom;
          scrollerObj.insertBefore(pullDownEl, scrollerObj.firstChild);
          pullDownOffset = $(pullDownEl).height();
          hidePullDownEl(0, false);
        }
      }

      if (theAdapter.needPullUpFooter) {
        var tempFooter = theAdapter.needPullUpFooter();
        if (tempFooter) {
          footerData = tempFooter;
          pullUpEl = footerData.dom;
          footerData["display"] = $(pullUpEl).css("display");
          $(pullUpEl).css("display", "none").addClass('LSinitF').trigger('statuschanged', createStatus('LSinitF'));
          scrollerObj.appendChild(pullUpEl);
        }
      }

      me.notifyDatasetChage(true);

      me.myScroll = new IScroll(wrapperObj, iScrollConfig);

      me.myScroll.on('refresh', function() {
        if ((pullDownEl) && ($(pullDownEl).hasClass('LSLoading'))) {
          // pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
          if (this.y >= 0) {
            // The pull-down-bar is fully visible:
            // Hide it with a simple 250ms animation
            hidePullDownEl(250, true);
          } else if (this.y > -pullDownOffset) {
            // The pull-down-bar is PARTLY visible:
            // Set up a shorter animation to hide it

            // Firt calculate a new margin-top for pullDownEl that matches the current scroll position
            $(pullDownEl).css("margin-top", this.y + 'px');

            // CSS-trick to force webkit to render/update any CSS-changes immediately: Access the offsetHeight property...
            pullDownEl.offsetHeight;

            // Calculate the animation time (shorter, dependant on the new distance to animate) from here to completely 'scrolledUp' (hidden)
            // Needs to be done before adjusting the scroll-positon (if we want to read this.y)
            var animTime = (250 * (pullDownOffset + this.y) / pullDownOffset);

            // Set scroll positon to top
            // (this is the same as adjusting the scroll postition to match the exact movement pullDownEl made due to the change of margin-top above, so the content will not "jump")
            this.scrollTo(0, 0, 0);

            // Hide pullDownEl with the new (shorter) animation (and reset the inline style again).
            setTimeout(function() {
              // Do this in a new thread to avoid glitches in iOS webkit (will make sure the immediate margin-top change above is rendered)...
              hidePullDownEl(animTime, true);
            }, 0);

          } else {
            // The pull-down-bar is completely off screen:
            // Hide it immediately
            hidePullDownEl(0, true);
            // And adjust the scroll postition to match the exact movement pullDownEl made due to change of margin-top above, so the content will not "jump"
            this.scrollBy(0, pullDownOffset, 0);
          }
        }
        if ((pullUpEl) && ($(pullUpEl).hasClass('LSLoadingF'))) {
          $(pullUpEl).removeClass('LSLoadingF').addClass('LSinitF').trigger('statuschanged', createStatus('LSinitF'));
        // pullUpEl.className = 'pullUp';
        // pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
        }
      });

      me.myScroll.on('scrollStart', function() {
        // Store the scroll starting point to be able to track movement in 'scroll' below
        scrollStartPos = this.y;
      });

      me.myScroll.on('scroll', function() {
        //经验值补丁
        if (pullDownEl || pullUpEl) {
          if ((scrollStartPos == 0) && (this.y == 0)) {
            // 'scroll' called, but scroller is not moving!
            // Probably because the content inside wrapper is small and fits the screen, so drag/scroll is disabled by iScroll

            // Fix this by a hack: Setting "myScroll.hasVerticalScroll=true" tricks iScroll to believe
            // that there is a vertical scrollbar, and iScroll will enable dragging/scrolling again...
            this.hasVerticalScroll = true;

            // Set scrollStartPos to -1000 to be able to detect this state later...
            scrollStartPos = -1000;
          } else if ((scrollStartPos == -1000) &&
            (((!pullUpEl) && (!$(pullDownEl).hasClass('flip')) && (this.y < 0)) ||
            ((!pullDownEl) && (!$(pullUpEl).hasClass('flip')) && (this.y > 0)))) {
            // Scroller was not moving at first (and the trick above was applied), but now it's moving in the wrong direction.
            // I.e. the user is either scrolling up while having no "pull-up-bar",
            // or scrolling down while having no "pull-down-bar" => Disable the trick again and reset values...
            this.hasVerticalScroll = false;
            scrollStartPos = 0;
            this.scrollBy(0, -this.y, 0); // Adjust scrolling position to undo this "invalid" movement
          }
        }
        //处理拖拽下拉的逻辑
        if (pullDownEl && !$(pullDownEl).hasClass('LSLoading')) {
          if (this.y > pullDownOffset + pullThreshold && !$(pullDownEl).hasClass('LSflip')) {
            // Adjust scrolling position to match the change in pullDownEl's margin-top
            showPullDownElNow('LSflip');
            this.scrollBy(0, -pullDownOffset, 0);
          // pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh...';
          } else if (this.y < 0 && $(pullDownEl).hasClass('LSflip')) { // User changes his mind...
            hidePullDownEl(0, false);
            this.scrollBy(0, pullDownOffset, 0); // Adjust scrolling position to match the change in pullDownEl's margin-top
          // pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
          }
        }
        //处理上拉的逻辑
        if (pullUpEl && !$(pullUpEl).hasClass('LSLoadingF')) {
          if (this.y < (this.maxScrollY - pullThreshold) && !$(pullUpEl).hasClass('LSflipF')) {
            $(pullUpEl).removeClass('LSLoadingF LSinitF').addClass('LSflipF').trigger('statuschanged', createStatus('LSflipF'));
          // $(pullUpEl).find('.pullUpLabel').text('Release to load more...');
          } else if (this.y > (this.maxScrollY + pullThreshold) && $(pullUpEl).hasClass('LSflipF')) {
            $(pullUpEl).removeClass('LSflipF LSLoadingF').addClass('LSinitF').trigger('statuschanged', createStatus('LSinitF'));
          // pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
          }
        }
      });

      me.myScroll.on('scrollEnd', function() {
        if ((pullDownEl) && $(pullDownEl).hasClass('LSflip')) {
          showPullDownElNow('LSLoading');
          // pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';
          theAdapter.onPullDown(me); // Execute custom function (ajax call?)
        }
        if ((pullUpEl) && ($(pullUpEl).css("display") != "none") && $(pullUpEl).hasClass('LSflipF')) {
          $(pullUpEl).removeClass('LSflipF LSinitF').addClass('LSLoadingF').trigger('statuschanged', createStatus('LSLoadingF'));
          // $(pullUpEl).attr('class', 'pullUp loading');
          // pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';
          theAdapter.onPullUp(me); // Execute custom function (ajax call?)
        }
        if (scrollStartPos = -1000) {
          // If scrollStartPos=-1000: Recalculate the true value of "hasVerticalScroll" as it may have been
          // altered in 'scroll' to enable pull-to-refresh/load when the content fits the screen...
          this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
        }
      });
      if (listAdapter.onInitFinish) {
        listAdapter.onInitFinish(me);
      }
    }
    setTimeout(init, 100);
  };
  //options.probeType参见 http://iscrolljs.com/#onscroll
  //header的class状态包括 LSinit-初始状态, LSLoading-加载中, LSflip-拖拽中
  //footer的class状态包括 LSinitF-初始状态, LSLoadingF-加载中, LSflipF-拖拽中
  $.fn.iscrollListView = function(iScrollConfig, listAdapter) {
    var theConfig = $.isEmptyObject(iScrollConfig) ? {
      probeType: 2,
      bounceTime: 250,
      bounceEasing: 'quadratic',
      mouseWheel: false,
      scrollbars: false,
      fadeScrollbars: true,
      preventDefault: false,
      probeType: 2,
      interactiveScrollbars: false
    } : iScrollConfig;
    return this.each(function(index, dom) {
      if ($.checkType(listAdapter, 'configItem', 'function')
        && $.checkType(listAdapter, 'needDataCount', 'function')) {
        //给div添加z-index
        $(dom).css("z-index", 1).css("position", "relative").css("overflow", "hidden");
        var adapter = {
          onInitFinish: (listAdapter.onInitFinish ? listAdapter.onInitFinish : null),
          onPullDown: (listAdapter.onPullDown ? listAdapter.onPullDown : function(theScroller) {
            theScroller.notifyDatasetChage();
          }),
          onPullUp: (listAdapter.onPullUp ? listAdapter.onPullUp : function(theScroller) {
            theScroller.notifyDatasetChage();
          }),
          needPullDownHeader: (listAdapter.needPullDownHeader ? listAdapter.needPullDownHeader : null),
          needPullUpFooter: (listAdapter.needPullUpFooter ? listAdapter.needPullUpFooter : null),
          hasMoreData: (listAdapter.hasMoreData ? listAdapter.hasMoreData : function() {
            return false;
          }),
          needDataCount: listAdapter.needDataCount,
          needLineHeight: (listAdapter.needLineHeight ? listAdapter.needLineHeight : function() {
            return 1;
          }),
          configItem: listAdapter.configItem
        };
        return new IScrollPullUpDown(dom, theConfig, adapter);
      } else {
        return null;
      }
    });
  }
})(window.jQuery || window.Zepto);
